﻿Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices
Imports Runa.Foundation.Threading
Imports System.Threading.Tasks

Namespace Global.Runa.UI.Threading

    Public Delegate Sub ContextCallback(ByVal state As Object)

    Public Class DispatcherOperation
        Public Event Aborted As EventHandler
        Public Event Completed As EventHandler

        Public Property Priority() As DispatcherPriority
        Public Property Exception() As Exception

        Public ReadOnly Property Dispatcher() As Dispatcher
            Get
                Return Me.m_Dispatcher
            End Get
        End Property

        Public ReadOnly Property Result() As Object
            Get
                Return Me.m_Result
            End Get
        End Property

        Public ReadOnly Property Status() As DispatcherOperationStatus
            Get
                Return Me.m_Status
            End Get
        End Property

        Public ReadOnly Property Task() As Task
            Get
                Return Me.m_Task
            End Get
        End Property

        Private ReadOnly Property DispatcherLock() As Object
            Get
                Return Me.m_DispatcherLock
            End Get
        End Property

        Friend ReadOnly Property Name() As String
            Get
                Return Me.m_Name
            End Get
        End Property

        Friend _taskSource As DispatcherOperationTaskSource

        Private m_Dispatcher As Dispatcher
        Private m_Result As Object
        Private m_Status As DispatcherOperationStatus
        Private m_Task As Task
        Private m_Name As String
        Private m_method As [Delegate]

        Friend m_DispatcherLock As Object

        Private _args As Object
        Private _numArgs As Integer
        Private _useAsyncSemantics As Boolean

        Friend Sub New(ByVal dispatcher As Dispatcher, ByVal priority As DispatcherPriority, ByVal action As Action)
            Me.New(dispatcher, action, priority, Nothing, 0, New DispatcherOperationTaskSource(Of Object), True)
        End Sub

        Friend Sub New(ByVal dispatcher As Dispatcher, ByVal priority As DispatcherPriority, ByVal method As [Delegate], ByVal args As Object())
            Me.New(dispatcher, method, priority, args, -1, New DispatcherOperationTaskSource(Of Object), True)
        End Sub

        Friend Sub New(ByVal dispatcher As Dispatcher, ByVal method As [Delegate], ByVal priority As DispatcherPriority, ByVal args As Object, ByVal numArgs As Integer)
            Me.New(dispatcher, method, priority, args, numArgs, New DispatcherOperationTaskSource(Of Object), False)
        End Sub

        Friend Sub New(ByVal dispatcher As Dispatcher, ByVal method As [Delegate], ByVal priority As DispatcherPriority, ByVal args As Object, ByVal numArgs As Integer, ByVal taskSource As DispatcherOperationTaskSource, ByVal useAsyncSemantics As Boolean)
            Me.m_Dispatcher = dispatcher
            Me.m_method = method
            Me._Priority = priority
            Me._numArgs = numArgs
            Me._args = args
            Me._taskSource = taskSource
            Me._taskSource.Initialize(Me)
            Me._useAsyncSemantics = useAsyncSemantics
        End Sub

        Friend Sub Invoke()
            Me.m_Result = Me.m_method.DynamicInvoke(CType(Me._args, Object()))
        End Sub

        Friend Sub InvokeCompletions()
            Select Case Me.Status
                Case DispatcherOperationStatus.Aborted
                    Me._taskSource.SetCanceled()
                    Return
                Case DispatcherOperationStatus.Completed
                    If (Me._Exception Is Nothing) Then
                        Me._taskSource.SetResult(Me.m_Result)
                        Return
                    End If
                    Me._taskSource.SetException(Me._Exception)
                    Return
            End Select
            'Debug.Print("Operation should be either Aborted or Completed!")
        End Sub

        Public Function Abort() As Boolean
            Dim flag As Boolean = False
            If (Not Me.Dispatcher Is Nothing) Then
                flag = Me.Dispatcher.Abort(Me)
                If flag = True Then
                    Me._taskSource.SetCanceled()
                    RaiseEvent Aborted(Me, EventArgs.Empty)
                End If
            End If
            Return flag
        End Function

        Public Function Wait() As DispatcherOperationStatus
            Return Me.Wait(TimeSpan.FromMilliseconds(-1))
        End Function

        Public Function Wait(ByVal timeout As TimeSpan) As DispatcherOperationStatus
            If (((Me.Status = DispatcherOperationStatus.Pending) OrElse (Me.Status = DispatcherOperationStatus.Executing)) AndAlso (timeout.TotalMilliseconds <> 0)) Then
                If (Me.Dispatcher.Thread Is Thread.CurrentThread) Then
                    If (Me.Status = DispatcherOperationStatus.Executing) Then
                        Throw New InvalidOperationException("ThreadMayNotWaitOnOperationsAlreadyExecutingOnTheSameThread")
                    End If
                    Dim frame As New DispatcherOperationFrame(Me, timeout)
                    Dispatcher.PushFrame(frame)
                Else
                    Dim doe = New DispatcherOperationEvent(Me, timeout)
                    doe.WaitOne()
                End If
            End If
            If (Me._useAsyncSemantics AndAlso ((Me.Status = DispatcherOperationStatus.Completed) OrElse (Me.Status = DispatcherOperationStatus.Aborted))) Then
                Me.Task.RunSynchronously()
            End If
            Return Me.Status
        End Function

    End Class

End Namespace